package com.blog4j.article.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blog4j.api.client.FeignUser;
import com.blog4j.api.vo.UserInfoVo;
import com.blog4j.article.component.AsyncService;
import com.blog4j.article.entity.ArticleEntity;
import com.blog4j.article.entity.CategoryEntity;
import com.blog4j.article.entity.CategoryLabelRelEntity;
import com.blog4j.article.entity.LabelEntity;
import com.blog4j.article.mapper.ArticleMapper;
import com.blog4j.article.mapper.CategoryLabelRelMapper;
import com.blog4j.article.mapper.CategoryMapper;
import com.blog4j.article.mapper.LabelMapper;
import com.blog4j.article.service.CategoryService;
import com.blog4j.article.vo.req.CategoryByUserIdReqVo;
import com.blog4j.article.vo.req.CategoryEditReqVo;
import com.blog4j.article.vo.req.CategoryListReqVo;
import com.blog4j.article.vo.req.CreateCategoryReqVo;
import com.blog4j.article.vo.req.SaveCategoryLabelReqVo;
import com.blog4j.article.vo.resp.CategoryRespVo;
import com.blog4j.common.enums.ApproveEnum;
import com.blog4j.common.enums.ArticleStatusEnum;
import com.blog4j.common.enums.CategoryAndLabelStatusEnum;
import com.blog4j.common.enums.ErrorEnum;
import com.blog4j.common.exception.Blog4jException;
import com.blog4j.common.utils.CommonUtil;
import com.blog4j.common.utils.IdGeneratorSnowflakeUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author 98k灬
 * @version v1.0.0
 * @Description : 功能描述
 * @Create on : 2024/6/27 22:10
 **/
@Service
@RequiredArgsConstructor(onConstructor_ = @Autowired)
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, CategoryEntity> implements CategoryService {
    private final ArticleMapper articleMapper;
    private final FeignUser feignUser;
    private final LabelMapper labelMapper;
    private final CategoryLabelRelMapper categoryLabelRelMapper;
    private final AsyncService asyncService;

    /**
     * 获取文章分类信息列表
     *
     * @param reqVo 查询条件
     * @return 文章分类信息
     */
    @Override
    public List<CategoryEntity> listCategory(CategoryListReqVo reqVo) {
        String categoryName = reqVo.getCategoryName();
        Integer status = reqVo.getStatus();
        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(CategoryEntity::getCreateTime);
        if (StringUtils.isNotBlank(categoryName)) {
            wrapper.like(CategoryEntity::getCategoryName, categoryName);
        }
        if (Objects.nonNull(status)) {
            wrapper.eq(CategoryEntity::getStatus, status);
        }
        if (Objects.nonNull(reqVo.getPageNo()) && Objects.nonNull(reqVo.getPageSize())) {
            PageHelper.startPage(reqVo.getPageNo(), reqVo.getPageSize());
        }
        return this.baseMapper.selectList(wrapper);
    }

    /**
     * 删除分类信息
     *
     * @param ids 待删除的分类ID列表
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteCategory(List<String> ids) {
        ids.forEach(id -> {
            CategoryEntity category = this.baseMapper.selectById(id);
            if (Objects.isNull(category)) {
                throw new Blog4jException(ErrorEnum.CATEGORY_INFO_EMPTY_ERROR);
            }

            LambdaQueryWrapper<ArticleEntity> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(ArticleEntity::getCategoryId, id);
            List<ArticleEntity> articleList = articleMapper.selectList(wrapper);
            if (CollectionUtil.isNotEmpty(articleList)) {
                throw new Blog4jException(ErrorEnum.CATEGORY_BIND_ARTICLE_ERROR);
            }
        });

        this.baseMapper.deleteBatchIds(ids);
        LambdaQueryWrapper<CategoryLabelRelEntity> wrapper = new LambdaQueryWrapper<CategoryLabelRelEntity>()
                .in(CategoryLabelRelEntity::getCategoryId, ids);
        categoryLabelRelMapper.delete(wrapper);
    }

    /**
     * 创建分类信息
     *
     * @param reqVo 分类信息
     */
    @Override
    public void create(CreateCategoryReqVo reqVo) {
        Integer status = reqVo.getStatus();
        if (!Objects.equals(status, CategoryAndLabelStatusEnum.DISABLE.getCode()) &&
            !Objects.equals(status, CategoryAndLabelStatusEnum.NORMAL.getCode())) {
            throw new Blog4jException(ErrorEnum.INVALID_PARAMETER_ERROR);
        }

        String userId = StpUtil.getLoginIdAsString();
        UserInfoVo user = feignUser.getUserInfoByUserId(userId);

        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<CategoryEntity>()
                .eq(CategoryEntity::getCreater, userId)
                .eq(CategoryEntity::getCategoryName, reqVo.getCategoryName());
        if (this.baseMapper.selectCount(wrapper) > 0) {
            throw new Blog4jException(ErrorEnum.CATEGORY_NAME_REPEAT_ERROR);
        }

        LambdaQueryWrapper<CategoryEntity> wrapper1 = new LambdaQueryWrapper<CategoryEntity>()
                .eq(CategoryEntity::getCreater, userId)
                .eq(CategoryEntity::getCategoryCode, reqVo.getCategoryCode());
        if (this.baseMapper.selectCount(wrapper1) > 0) {
            throw new Blog4jException(ErrorEnum.CATEGORY_CODE_REPEAT_ERROR);
        }

        CategoryEntity category = new CategoryEntity();
        BeanUtils.copyProperties(reqVo, category);
        category.setUpdateTime(CommonUtil.getCurrentDateTime())
                .setCreateTime(CommonUtil.getCurrentDateTime())
                .setCategoryId(IdGeneratorSnowflakeUtil.snowflakeId())
                .setCreater(userId)
                .setCreaterName(user.getUserName())
                .setStatus(status);
        this.baseMapper.insert(category);
    }

    /**
     * 根据创建者ID查询分类列表
     *
     * @param userId 创建者ID
     * @return 分类列表
     */
    @Override
    public List<CategoryEntity> getInfoByCreaterId(String userId) {
        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<CategoryEntity>()
                .eq(CategoryEntity::getCreater, userId)
                .eq(CategoryEntity::getStatus, CategoryAndLabelStatusEnum.NORMAL.getCode());
        return this.baseMapper.selectList(wrapper);
    }

    /**
     * 编辑分类信息
     *
     * @param reqVo 分类信息
     */
    @Override
    public void edit(CategoryEditReqVo reqVo) {
        String categoryId = reqVo.getCategoryId();
        String categoryName = reqVo.getCategoryName();
        CategoryEntity category = this.baseMapper.selectById(categoryId);
        if (Objects.isNull(category)) {
            throw new Blog4jException(ErrorEnum.CATEGORY_INFO_EMPTY_ERROR);
        }

        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<CategoryEntity>()
                .ne(CategoryEntity::getCreater, category.getCreater())
                .eq(CategoryEntity::getCategoryName, reqVo.getCategoryName());
        if (this.baseMapper.selectCount(wrapper) > 0) {
            throw new Blog4jException(ErrorEnum.CATEGORY_NAME_REPEAT_ERROR);
        }

        LambdaQueryWrapper<CategoryEntity> wrapper1 = new LambdaQueryWrapper<CategoryEntity>()
                .ne(CategoryEntity::getCreater, category.getCreater())
                .eq(CategoryEntity::getCategoryCode, reqVo.getCategoryCode());
        if (this.baseMapper.selectCount(wrapper1) > 0) {
            throw new Blog4jException(ErrorEnum.CATEGORY_CODE_REPEAT_ERROR);
        }

        CategoryEntity categoryEntity = new CategoryEntity();
        BeanUtils.copyProperties(reqVo, categoryEntity);
        categoryEntity.setUpdateTime(CommonUtil.getCurrentDateTime());
        this.baseMapper.updateById(categoryEntity);

        // 异步更新文章信息中的分类信息
        if (!StringUtils.equals(categoryName, category.getCategoryName())) {
            asyncService.updateArticleCategoryName(categoryId, categoryName);
        }
    }

    /**
     * 保存分类下的标签信息
     *
     * @param reqVo 请求信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveCategoryLabel(SaveCategoryLabelReqVo reqVo) {
        String categoryId = reqVo.getCategoryId();
        List<String> labelIds = reqVo.getLabelIds();

        CategoryEntity category = this.baseMapper.selectById(categoryId);
        if (Objects.isNull(category)) {
            throw new Blog4jException(ErrorEnum.CATEGORY_INFO_EMPTY_ERROR);
        }

        List<LabelEntity> labelList = labelMapper.selectBatchIds(labelIds);
        if (CollectionUtil.size(labelIds) != CollectionUtil.size(labelList)) {
            throw new Blog4jException(ErrorEnum.INVALID_PARAMETER_ERROR);
        }

        // 现将该分类下的所有标签全部删除
        LambdaQueryWrapper<CategoryLabelRelEntity> wrapper = new LambdaQueryWrapper<CategoryLabelRelEntity>()
                .eq(CategoryLabelRelEntity::getCategoryId, categoryId);
        categoryLabelRelMapper.delete(wrapper);

        // todo 性能优化
        for (LabelEntity label : labelList) {
            CategoryLabelRelEntity build = CategoryLabelRelEntity.builder()
                    .labelId(label.getLabelId())
                    .categoryId(categoryId)
                    .build();
            categoryLabelRelMapper.insert(build);
        }
    }

    /**
     * 门户端查询分类列表
     *
     * @param reqVo 请求信息
     * @return 分类列表
     */
    @Override
    public PageInfo<CategoryRespVo> listCategoryWeb(CategoryListReqVo reqVo) {
        Integer pageNo = reqVo.getPageNo();
        Integer pageSize = reqVo.getPageSize();

        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<CategoryEntity>()
                .eq(CategoryEntity::getStatus, CategoryAndLabelStatusEnum.NORMAL.getCode())
                .orderByDesc(CategoryEntity::getCreateTime);
        Page<Object> page = PageHelper.startPage(pageNo, pageSize);
        List<CategoryEntity> categoryList = this.baseMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(categoryList)) {
            return new PageInfo<>();
        }

        List<CategoryRespVo> categoryRespVoList = categoryList.stream().map(item -> {
            CategoryRespVo categoryRespVo = new CategoryRespVo();
            BeanUtils.copyProperties(item, categoryRespVo);
            return categoryRespVo;
        }).collect(Collectors.toList());

        Set<String> categoryIdSet = categoryList.stream().map(CategoryEntity::getCategoryId).collect(Collectors.toSet());

        LambdaQueryWrapper<ArticleEntity> wrapper1 = new LambdaQueryWrapper<ArticleEntity>()
                .in(ArticleEntity::getCategoryId, categoryIdSet)
                .eq(ArticleEntity::getStatus, ArticleStatusEnum.ONLINE.getCode())
                .eq(ArticleEntity::getApproveStatus, ApproveEnum.PASS.getCode());
        List<ArticleEntity> articleList = articleMapper.selectList(wrapper1);
        if (CollectionUtil.isNotEmpty(articleList)) {
            for (CategoryRespVo categoryRespVo : categoryRespVoList) {
                List<ArticleEntity> articleEntityList = articleList.stream()
                        .filter(item -> StringUtils.equals(categoryRespVo.getCategoryId(), item.getCategoryId())).collect(Collectors.toList());
                categoryRespVo.setArticleCount(articleEntityList.size());
            }
        }

        PageInfo<CategoryRespVo> pageInfo = new PageInfo<>(categoryRespVoList);
        pageInfo.setTotal(page.getTotal());
        return pageInfo;
    }

    /**
     * 根据用户ID获取分类信息列表
     *
     * @param reqVo 请求信息
     * @return 分类信息列表
     */
    @Override
    public PageInfo<CategoryRespVo> getCategoryByUserId(CategoryByUserIdReqVo reqVo) {
        String userId = reqVo.getUserId();
        Integer pageNo = reqVo.getPageNo();
        Integer pageSize = reqVo.getPageSize();

        UserInfoVo userInfoVo = feignUser.getUserInfoByUserId(userId);
        if (StringUtils.isBlank(userInfoVo.getUserId())) {
            throw new Blog4jException(ErrorEnum.USER_NOT_EXIST_ERROR);
        }

        Page<Object> page = PageHelper.startPage(pageNo, pageSize);
        LambdaQueryWrapper<CategoryEntity> wrapper = new LambdaQueryWrapper<CategoryEntity>()
                .eq(CategoryEntity::getCreater, userId)
                .eq(CategoryEntity::getStatus, CategoryAndLabelStatusEnum.NORMAL.getCode());
        List<CategoryEntity> categoryList = this.baseMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(categoryList)) {
            return new PageInfo<>(new ArrayList<>());
        }

        List<CategoryRespVo> categoryRespVoList = categoryList.stream().map(item -> {
            CategoryRespVo categoryRespVo = new CategoryRespVo();
            BeanUtils.copyProperties(item, categoryRespVo);
            return categoryRespVo;
        }).collect(Collectors.toList());

        Set<String> categoryIdSet = categoryList.stream()
                .map(CategoryEntity::getCategoryId).collect(Collectors.toSet());
        LambdaQueryWrapper<ArticleEntity> queryWrapper = new LambdaQueryWrapper<ArticleEntity>()
                .eq(ArticleEntity::getStatus, ArticleStatusEnum.ONLINE.getCode())
                .eq(ArticleEntity::getApproveStatus, ApproveEnum.PASS.getCode())
                .in(ArticleEntity::getCategoryId, categoryIdSet);
        List<ArticleEntity> articleList = articleMapper.selectList(queryWrapper);

        for (CategoryRespVo categoryRespVo : categoryRespVoList) {
            String categoryId = categoryRespVo.getCategoryId();
            List<ArticleEntity> articleEntityList = articleList.stream().filter(item -> StringUtils.equals(categoryId, item.getCategoryId()))
                    .collect(Collectors.toList());
            categoryRespVo.setArticleCount(CollectionUtil.size(articleEntityList));
        }

        PageInfo<CategoryRespVo> pageInfo = new PageInfo<>(categoryRespVoList);
        pageInfo.setTotal(page.getTotal());
        return pageInfo;
    }
}
